<?php

namespace Core\annotationhandlers;

use Core\annotations\Redis;
use Core\BeanFactory;
use Core\init\DecoratorCollector;
use Core\lib\RedisHelper;
use Swoole\Coroutine;

/**
 * 取得Key
 * @param string $key
 * @param array $params
 * @return mixed|string
 */
function getKey(string $key, array $params)
{
    if (preg_match("/^#(\d+)/i", $key, $matches)) {
        return $params[$matches[1]];
    }
    return $key;
}

/**
 * 取得Key从data
 * @param string $key
 * @param array $arr
 * @return mixed|string
 */
function getKeyFromData(string $key, array $arr)
{
    if (preg_match("/^#(\w+)/i", $key, $matches)) {
        return $arr[$matches[1]];
    }
    return $key;
}


/**
 * String
 * @param $self  Redis
 * @param array $params
 * @param callable $func
 * @return mixed
 */
function redisByString(Redis $self, array $params, callable $func)
{
    $_key = $self->prefix . getKey($self->key, $params);

    $getFromRedis = RedisHelper::get($_key);
    if ($getFromRedis) {
        //echo "从redis读取";
        return $getFromRedis;
    } else {
        //echo "从DB读取";
        $getData = call_user_func($func, ... $params);
        if ($self->expire > 0) {
            RedisHelper::setex($_key, intval($self->expire), json_encode($getData));
        }
        RedisHelper::set($_key, json_encode($getData));
        return $getData;
    }
}

/**
 * Hash
 * @param Redis $self
 * @param array $params
 * @param callable $func
 * @return mixed
 */
function redisByHash(Redis $self, array $params, callable $func)
{
    $_key = $self->prefix . getKey($self->key, $params);
    $getFromRedis = RedisHelper::hGetAll($_key);
    if ($getFromRedis) {
        //echo "从redis读取";

        //自增 (可做点击量类型需求)
        if ($self->incr) {
            RedisHelper::hIncrBy($_key, $self->incr, 1);
        }

        return $getFromRedis;
    } else {
        //echo "从DB读取";
        $getData = call_user_func($func, ... $params);

        if (is_object($getData)) {
            $getData = json_decode(json_encode($getData), true);
        }

        //TODO:此处 $getData 必须是数组
        //如果是二维数组 则 循环插入
        if (count($getData, 1)) {
            foreach ($getData as $data) {
                RedisHelper::hMSet($self->prefix . getKeyFromData($self->key, $data), $data);
            }
        } else {
            //如果是一维数组则直接插入
            RedisHelper::hMGet($_key, $getData);
        }

        return $getData;
    }
}


/**
 * SortedSet
 * @param Redis $self
 * @param array $params
 * @param callable $func
 * @return  mixed|array
 */
function redisBySortedSet(Redis $self, array $params, callable $func)
{
    //是否为协程方式读取
    if ($self->coroutine) {

        /**
         * @var $channel \Swoole\Coroutine\Channel
         */
        $channel = call_user_func($func, ... $params);
        $getData = $channel->pop();

    } else {
        $getData = call_user_func($func, ... $params);
    }

    if (is_object($getData)) {
        $getData = json_decode(json_encode($getData), true);
    }

    foreach ($getData as $data) {
        RedisHelper::zAdd($self->prefix, $data[$self->score], $self->member, $data[$self->key]);
    }

    //防错处理
    return ['result' => 'success'];
}

/**
 * @param Redis $self
 * @param array $params
 * @param callable $func
 * @return mixed
 */
function redisByList(Redis $self, array $params, callable $func)
{
    $_key = $self->prefix . getKey($self->key, $params);
    $getFromRedis = RedisHelper::lget($_key);
    if ($getFromRedis) {
        //echo "从redis读取";
        return $getFromRedis;
    } else {
        //echo "从DB读取";
        $getData = call_user_func($func, ... $params);

        if (is_object($getData)) {
            $getData = json_decode(json_encode($getData), true);
        }
        RedisHelper::hMGet($_key, $getData);
        return $getData;
    }
}

/**
 * @param Redis $self
 * @param array $params
 * @param callable $func
 * @return mixed
 */
function RedisByLua($self, $params, $func)
{
    $r = RedisHelper::eval($self->script);
    return $r;
}

return [
    //装饰器 包装方法
    Redis::class => function (\ReflectionMethod $method, $instance, $self) {

        /**
         * @var  $d_collector  DecoratorCollector
         */
        $d_collector = BeanFactory::getBean(DecoratorCollector::class);

        $key = (get_class($instance) . '::' . $method->getName());

        //收集装饰器，放入装饰器收集类
        $d_collector->dSet[$key] = function ($func) use ($self) {

            return function ($params) use ($func, $self) {

                /**
                 * @var $self Redis
                 */
                /*if (!empty($self->key)) {
                    $_key = $self->prefix . getKey($self->key, $params);

                    $getFromRedis = RedisHelper::get($_key);
                    if ($getFromRedis) {
                        echo "从redis读取";
                        return $getFromRedis;
                    } else {
                        echo "从DB读取";
                        $getData = call_user_func($func, ... $params);
                        RedisHelper::set($_key, json_encode($getData));
                        return $getData;
                    }
                }*/

                if ($self->script != '') {
                    return RedisByLua($self, $params, $func);
                }

                if ($self->key != '') {
                    switch ($self->key) {
                        case 'string' :
                            return redisByString($self, $params, $func);
                            break;
                        case 'list' :
                            return redisByList($self, $params, $func);
                            break;
                        case 'hash' :
                            return redisByHash($self, $params, $func);
                            break;
                        case 'set' :
                            break;
                        case 'sortedset' :
                            return redisBySortedSet($self, $params, $func);
                            break;
                        case 'zset' :
                            break;

                        default :
                            return call_user_func($func, ... $params);
                    }
                }

                return call_user_func($func, ... $params);
            };
        };

        return $instance;
    }
];